home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 4: GNU Archives / Linux Cubed Series 4 - GNU Archives.iso / gnu / enscript.4 / enscript / enscript-1.4.0 / src / util.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-06-14  |  34.7 KB  |  1,712 lines

  1. /* 
  2.  * Help utilities.
  3.  * Copyright (c) 1995 Markku Rossi.
  4.  *
  5.  * Author: Markku Rossi <mtr@iki.fi>
  6.  */
  7.  
  8. /*
  9.  * This file is part of GNU enscript.
  10.  * 
  11.  * This program is free software; you can redistribute it and/or modify
  12.  * it under the terms of the GNU General Public License as published by
  13.  * the Free Software Foundation; either version 2, or (at your option)
  14.  * any later version.
  15.  *
  16.  * This program is distributed in the hope that it will be useful,
  17.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  18.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  19.  * GNU General Public License for more details.
  20.  *
  21.  * You should have received a copy of the GNU General Public License
  22.  * along with this program; see the file COPYING.  If not, write to
  23.  * the Free Software Foundation, 59 Temple Place - Suite 330,
  24.  * Boston, MA 02111-1307, USA.
  25.  */
  26.  
  27. #include "gsint.h"
  28.  
  29. /*
  30.  * Static variables.
  31.  */
  32.  
  33. /* 7bit ASCII scand encodings (additions to 7bit ASCII enc). */
  34. static struct
  35. {
  36.   int code;
  37.   char *name;
  38. } enc_7bit_ascii_scands[] =
  39. {
  40.   {'{',        "adieresis"},
  41.   {'|',        "odieresis"},
  42.   {'}',        "aring"},
  43.   {'[',        "Adieresis"},
  44.   {'\\',    "Odieresis"},
  45.   {']',        "Aring"},
  46.   {0, NULL},
  47. };
  48.  
  49.  
  50. /*
  51.  * Prototypes for static functions.
  52.  */
  53.  
  54. static void cfg_fatal __P ((char *file, int line, char *fmt, ...));
  55.  
  56.  
  57. /*
  58.  * Global functions.
  59.  */
  60.  
  61. void
  62. #if defined(HAVE_STDARG_H) && defined(__STDC__) && __STDC__
  63.   message (int verbose_level, char *fmt, ...)
  64. #else
  65.   message (verbose_level, va_alist)
  66.      int verbose_level;
  67.      va_dcl
  68. #endif
  69. {
  70.   va_list args;
  71.  
  72. #if defined(HAVE_STDARG_H) && defined(__STDC__) && __STDC__
  73.   va_start (args, fmt);
  74. #else
  75.   char *fmt;
  76.  
  77.   va_start (args);
  78.   fmt = va_arg (args, char *);
  79. #endif
  80.  
  81.   if (quiet || verbose < verbose_level)
  82.     return;
  83.  
  84. #if HAVE_VPRINTF
  85.   vfprintf (stderr, fmt, args);
  86. #else
  87.   _doprnt (fmt, args, stderr);
  88. #endif
  89.  
  90.   va_end (args);
  91. }
  92.  
  93.  
  94. void
  95. #if defined(HAVE_STDARG_H) && defined(__STDC__) && __STDC__
  96.   error (char *fmt, ...)
  97. #else
  98.   error (va_alist)
  99.      va_dcl
  100. #endif
  101. {
  102.   va_list args;
  103. #if defined(HAVE_STDARG_H) && defined(__STDC__) && __STDC__
  104.   va_start (args, fmt);
  105. #else
  106.   char *fmt;
  107.  
  108.   va_start (args);
  109.   fmt = va_arg (args, char *);
  110. #endif
  111.  
  112.   fprintf (stderr, "%s: ", program);
  113.  
  114. #if HAVE_VPRINTF
  115.   vfprintf (stderr, fmt, args);
  116. #else
  117.   _doprnt (fmt, args, stderr);
  118. #endif
  119.   va_end (args);
  120.  
  121.   fprintf (stderr, "\n");
  122.   fflush (stderr);
  123. }
  124.  
  125.  
  126. void
  127. #if defined(HAVE_STDARG_H) && defined(__STDC__) && __STDC__
  128.   fatal (char *fmt, ...)
  129. #else
  130.   fatal (va_alist)
  131.      va_dcl
  132. #endif
  133. {
  134.   va_list args;
  135. #if defined(HAVE_STDARG_H) && defined(__STDC__) && __STDC__
  136.   va_start (args, fmt);
  137. #else
  138.   char *fmt;
  139.  
  140.   va_start (args);
  141.   fmt = va_arg (args, char *);
  142. #endif
  143.  
  144.   fprintf (stderr, "%s: ", program);
  145.  
  146. #if HAVE_VPRINTF
  147.   vfprintf (stderr, fmt, args);
  148. #else
  149.   _doprnt (fmt, args, stderr);
  150. #endif
  151.   va_end (args);
  152.  
  153.   fprintf (stderr, "\n");
  154.   fflush (stderr);
  155.  
  156.   exit (1);
  157. }
  158.  
  159. #define GET_TOKEN(from) (strtok ((from), " \t\n"))
  160. #define GET_LINE_TOKEN(from) (strtok ((from), "\n"))
  161.  
  162. #define CHECK_TOKEN()                             \
  163.   if (token2 == NULL)                             \
  164.     cfg_fatal (fname, line, _("missing argument: %s"), token);
  165.  
  166. int
  167. read_config (char *path, char *file)
  168. {
  169.   FILE *fp;
  170.   char fname[512];
  171.   char buf[4096];
  172.   char *token, *token2;
  173.   int line = 0;
  174.  
  175.   sprintf (fname, "%s/%s", path, file);
  176.   fp = fopen (fname, "r");
  177.   if (fp == NULL)
  178.     return 0;
  179.  
  180.   while (fgets (buf, sizeof (buf), fp))
  181.     {
  182.       line++;
  183.  
  184.       if (buf[0] == '#')
  185.     continue;
  186.       
  187.       token = GET_TOKEN (buf);
  188.       if (token == NULL)
  189.     /* Empty line. */
  190.     continue;
  191.  
  192.       if (MATCH (token, "AcceptCompositeCharacters:"))
  193.     {
  194.       token2 = GET_TOKEN (NULL);
  195.       CHECK_TOKEN ();
  196.       accept_composites = atoi (token2);
  197.     }
  198.       else if (MATCH (token, "AFMPath:"))
  199.     {
  200.       token2 = GET_TOKEN (NULL);
  201.       CHECK_TOKEN ();
  202.       strcpy (afm_path_buffer, token2);
  203.       afm_path = afm_path_buffer;
  204.     }
  205.       else if (MATCH (token, "AppendCtrlD:"))
  206.     {
  207.       token2 = GET_TOKEN (NULL);
  208.       CHECK_TOKEN ();
  209.       append_ctrl_D = atoi (token2);
  210.     }
  211.       else if (MATCH (token, "Clean7Bit:"))
  212.     {
  213.       token2 = GET_TOKEN (NULL);
  214.       CHECK_TOKEN ();
  215.       clean_7bit = atoi (token2);
  216.     }
  217.       else if (MATCH (token, "DefaultEncoding:"))
  218.     {
  219.       token2 = GET_TOKEN (NULL);
  220.       CHECK_TOKEN ();
  221.       strcpy (encoding_name_buffer, token2);
  222.       encoding_name = encoding_name_buffer;
  223.     }
  224.       else if (MATCH (token, "DefaultFancyHeader:"))
  225.     {
  226.       token2 = GET_TOKEN (NULL);
  227.       CHECK_TOKEN ();
  228.       strcpy (fancy_header_default, token2);
  229.     }
  230.       else if (MATCH (token, "DefaultMedia:"))
  231.     {
  232.       token2 = GET_TOKEN (NULL);
  233.       CHECK_TOKEN ();
  234.       strcpy (media_name_buffer, token2);
  235.       media_name = media_name_buffer;
  236.     }
  237.       else if (MATCH (token, "DefaultOutputMethod:"))
  238.     {
  239.       token2 = GET_TOKEN (NULL);
  240.       CHECK_TOKEN ();
  241.       if (MATCH (token2, "printer"))
  242.         output_file = OUTPUT_FILE_NONE;
  243.       else if (MATCH (token2, "stdout"))
  244.         output_file = OUTPUT_FILE_STDOUT;
  245.       else
  246.         cfg_fatal (fname, line, _("illegal value \"%s\" for option %s"),
  247.                token2, token);
  248.     }
  249.       else if (MATCH (token, "DownloadFont:"))
  250.     {
  251.       token2 = GET_TOKEN (NULL);
  252.       CHECK_TOKEN ();
  253.       strhash_put (download_fonts, token2, strlen (token2) + 1, NULL,
  254.                NULL);
  255.     }
  256.       else if (MATCH (token, "EscapeChar:"))
  257.     {
  258.       token2 = GET_TOKEN (NULL);
  259.       CHECK_TOKEN ();
  260.       escape_char = atoi (token2);
  261.       if (escape_char < 0 || escape_char > 255)
  262.         cfg_fatal (fname, line, _("invalid value \"%s\" for option %s"),
  263.                token2, token);
  264.     }
  265.       else if (MATCH (token, "FormFeedType:"))
  266.     {
  267.       token2 = GET_TOKEN (NULL);
  268.       CHECK_TOKEN ();
  269.       if (MATCH (token2, "column"))
  270.         formfeed_type = FORMFEED_COLUMN;
  271.       else if (MATCH (token2, "page"))
  272.         formfeed_type = FORMFEED_PAGE;
  273.       else
  274.         cfg_fatal (fname, line, _("illegal value \"%s\" for option %s"),
  275.                token2, token);
  276.     }
  277.       else if (MATCH (token, "HighlightBarGray:"))
  278.     {
  279.       token2 = GET_TOKEN (NULL);
  280.       CHECK_TOKEN ();
  281.       highlight_bar_gray = atof (token2);
  282.     }
  283.       else if (MATCH (token, "HighlightBars:"))
  284.     {
  285.       token2 = GET_TOKEN (NULL);
  286.       CHECK_TOKEN ();
  287.       highlight_bars = atoi (token2);
  288.     }
  289.       else if (MATCH (token, "LibraryPath:"))
  290.     {
  291.       token2 = GET_TOKEN (NULL);
  292.       CHECK_TOKEN ();
  293.       strcpy (libpath, token2);
  294.     }
  295.       else if (MATCH (token, "Media:"))
  296.     {
  297.       char *name;
  298.       int w, h, llx, lly, urx, ury;
  299.  
  300.       token2 = GET_TOKEN (NULL);
  301.       CHECK_TOKEN ();
  302.       name = token2;
  303.  
  304.       token2 = GET_TOKEN (NULL);
  305.       CHECK_TOKEN ();
  306.       w = atoi (token2);
  307.  
  308.       token2 = GET_TOKEN (NULL);
  309.       CHECK_TOKEN ();
  310.       h = atoi (token2);
  311.  
  312.       token2 = GET_TOKEN (NULL);
  313.       CHECK_TOKEN ();
  314.       llx = atoi (token2);
  315.  
  316.       token2 = GET_TOKEN (NULL);
  317.       CHECK_TOKEN ();
  318.       lly = atoi (token2);
  319.  
  320.       token2 = GET_TOKEN (NULL);
  321.       CHECK_TOKEN ();
  322.       urx = atoi (token2);
  323.  
  324.       token2 = GET_TOKEN (NULL);
  325.       CHECK_TOKEN ();
  326.       ury = atoi (token2);
  327.  
  328.       add_media (name, w, h, llx, lly, urx, ury);
  329.     }
  330.       else if (MATCH (token, "NonPrintableFormat:"))
  331.     {
  332.       token2 = GET_TOKEN (NULL);
  333.       CHECK_TOKEN ();
  334.       strcpy (npf_name_buf, token2);
  335.       npf_name = npf_name_buf;
  336.     }
  337.       else if (MATCH (token, "PageLabelFormat:"))
  338.     {
  339.       token2 = GET_TOKEN (NULL);
  340.       CHECK_TOKEN ();
  341.       strcpy (page_label_format_buf, token2);
  342.       page_label_format = page_label_format_buf;
  343.     }
  344.       else if (MATCH (token, "PagePrefeed:"))
  345.     {
  346.       token2 = GET_TOKEN (NULL);
  347.       CHECK_TOKEN ();
  348.       page_prefeed = atoi (token2);
  349.     }
  350.       else if (MATCH (token, "Printer:"))
  351.     {
  352.       token2 = GET_TOKEN (NULL);
  353.       CHECK_TOKEN ();
  354.       strcpy (printer_buf, token2);
  355.       printer = printer_buf;
  356.     }
  357.       else if (MATCH (token, "QueueParam:"))
  358.     {
  359.       token2 = GET_LINE_TOKEN (NULL);
  360.       CHECK_TOKEN ();
  361.       strcpy (queue_param, token2);
  362.     }
  363.       else if (MATCH (token, "SetPageDevice:"))
  364.     {
  365.       token2 = GET_TOKEN (NULL);
  366.       CHECK_TOKEN ();
  367.       parse_key_value_pair (pagedevice, token2);
  368.     }
  369.       else if (MATCH (token, "Spooler:"))
  370.     {
  371.       token2 = GET_TOKEN (NULL);
  372.       CHECK_TOKEN ();
  373.       strcpy (spooler_command, token2);
  374.     }
  375.       else if (MATCH (token, "StatusDict:"))
  376.     {
  377.       token2 = GET_TOKEN (NULL);
  378.       CHECK_TOKEN ();
  379.       parse_key_value_pair (statusdict, token2);
  380.     }
  381.       else if (MATCH (token, "Underlay:"))
  382.     {
  383.       token2 = GET_LINE_TOKEN (NULL);
  384.       CHECK_TOKEN ();
  385.       underlay = xmalloc (strlen (token2) + 1);
  386.       strcpy (underlay, token2);
  387.     }
  388.       else if (MATCH (token, "UnderlayAngle:"))
  389.     {
  390.       token2 = GET_TOKEN (NULL);
  391.       CHECK_TOKEN ();
  392.       ul_angle = atof (token2);
  393.       ul_angle_p = 1;
  394.     }
  395.       else if (MATCH (token, "UnderlayFont:"))
  396.     {
  397.       token2 = GET_TOKEN (NULL);
  398.       CHECK_TOKEN ();
  399.       if (!parse_font_spec (token2, &ul_font, &ul_ptsize))
  400.         cfg_fatal (fname, line, _("malformed font spec: %s"), token2);
  401.     }
  402.       else if (MATCH (token, "UnderlayGray:"))
  403.     {
  404.       token2 = GET_TOKEN (NULL);
  405.       CHECK_TOKEN ();
  406.       ul_gray = atof (token2);
  407.     }
  408.       else if (MATCH (token, "UnderlayPosition:"))
  409.     {
  410.       token2 = GET_TOKEN (NULL);
  411.       CHECK_TOKEN ();
  412.       strcpy (ul_position_buf, token2);
  413.       ul_position = ul_position_buf;
  414.       ul_position_p = 1;
  415.     }
  416.       else if (MATCH (token, "UnderlayStyle:"))
  417.     {
  418.       token2 = GET_TOKEN (NULL);
  419.       CHECK_TOKEN ();
  420.       strcpy (ul_style_str_buf, token2);
  421.       ul_style_str = ul_style_str_buf;
  422.     }
  423.       else
  424.     cfg_fatal (fname, line, _("illegal option: %s"), token);
  425.     }
  426.   return 1;
  427. }
  428.  
  429.  
  430. void
  431. add_media (char *name, int w, int h, int llx, int lly, int urx, int ury)
  432. {
  433.   MediaEntry *entry;
  434.  
  435.   message (2,
  436.        "add_media: name=%s, w=%d, h=%d, llx=%d, lly=%d, urx=%d, ury=%d\n",
  437.        name, w, h, llx, lly, urx, ury);
  438.  
  439.   entry = xcalloc (1, sizeof (*entry));
  440.   entry->name = xmalloc (strlen (name) + 1);
  441.  
  442.   strcpy (entry->name, name);
  443.   entry->w = w;
  444.   entry->h = h;
  445.   entry->llx = llx;
  446.   entry->lly = lly;
  447.   entry->urx = urx;
  448.   entry->ury = ury;
  449.  
  450.   entry->next = media_names;
  451.   media_names = entry;
  452. }
  453.  
  454.  
  455. void
  456. do_list_missing_characters (int *array)
  457. {
  458.   int i;
  459.   int count = 0;
  460.  
  461.   for (i = 0; i < 256; i++)
  462.     if (array[i])
  463.       {
  464.     fprintf (stderr, "%3d ", i);
  465.     count++;
  466.     if (count % 15 == 0)
  467.       fprintf (stderr, "\n");
  468.       }
  469.  
  470.   if (count % 15 != 0)
  471.     fprintf (stderr, "\n");
  472. }
  473.  
  474.  
  475. int
  476. file_existsp (char *name, char *suffix)
  477. {
  478.   FileLookupCtx ctx;
  479.  
  480.   strcpy (ctx.name, name);
  481.   strcpy (ctx.suffix, suffix ? suffix : "");
  482.  
  483.   return pathwalk (libpath, file_lookup, &ctx);
  484. }
  485.  
  486.  
  487. int
  488. paste_file (char *name, char *suffix)
  489. {
  490.   char buf[512];
  491.   char resources[512];
  492.   FILE *fp;
  493.   FileLookupCtx ctx;
  494.   int pending_comment = 0;
  495.   int line = 0;
  496.  
  497.   strcpy (ctx.name, name);
  498.   strcpy (ctx.suffix, suffix ? suffix : "");
  499.  
  500.   if (!pathwalk (libpath, file_lookup, &ctx))
  501.     return 0;
  502.   fp = fopen (ctx.fullname, "r");
  503.   if (fp == NULL)
  504.     return 0;
  505.  
  506.   /* Find the end of the header. */
  507. #define HDR_TAG "% -- code follows this line --"
  508.   while ((fgets (buf, sizeof (buf), fp)))
  509.     {
  510.       line++;
  511.       if (strncmp (buf, HDR_TAG, strlen (HDR_TAG)) == 0)
  512.     break;
  513.     }
  514.  
  515.   /* Dump rest of file. */
  516.   while ((fgets (buf, sizeof (buf), fp)))
  517.     {
  518.       line++;
  519.  
  520.       /*
  521.        * Document needed resources?
  522.        */
  523. #define RESOURCE_DSC     "%%DocumentNeededResources:"
  524. #define CONT_DSC     "%%+"
  525.       if (strncmp (buf, RESOURCE_DSC, strlen (RESOURCE_DSC)) == 0)
  526.     {
  527.       char *cp, *cp2;
  528.  
  529.       strcpy (resources, buf + strlen (RESOURCE_DSC));
  530.       pending_comment = 1;
  531.  
  532.     parse_resources:
  533.       /* Register needed resources. */
  534.       cp = GET_TOKEN (resources);
  535.       if (cp == NULL)
  536.         /* Get the next line. */
  537.         continue;
  538.  
  539.       if (MATCH (cp, "font"))
  540.         {
  541.           for (cp = GET_TOKEN (NULL); cp; cp = GET_TOKEN (NULL))
  542.         /* Is this font already known? */
  543.         if (!strhash_get (res_fonts, cp, strlen (cp) + 1,
  544.                   (void **) &cp2))
  545.           {
  546.             /* Not it is not,  we must include this resource. */
  547.             fprintf (ofp, "%%%%IncludeResource: font %s\n", cp);
  548.  
  549.             /*
  550.              * And register that this resource is needed in
  551.              * this document.
  552.              */
  553.             strhash_put (res_fonts, cp, strlen (cp) + 1, NULL, NULL);
  554.           }
  555.  
  556.           /* Do not pass this DSC row to the output. */
  557.           continue;
  558.         }
  559.       else
  560.         /* Unknown resource, ignore. */
  561.         continue;
  562.     }
  563.       else if (pending_comment
  564.            && strncmp (buf, CONT_DSC, strlen (CONT_DSC)) == 0)
  565.     {
  566.       strcpy (resources, buf + strlen (CONT_DSC));
  567.       goto parse_resources;
  568.     }
  569.       else
  570.     pending_comment = 0;
  571.  
  572.       /*
  573.        * `%Format' directive?
  574.        */
  575. #define DIRECTIVE_FORMAT "%Format:"
  576.       if (strncmp (buf, DIRECTIVE_FORMAT, strlen (DIRECTIVE_FORMAT)) == 0)
  577.     {
  578.       int i, j;
  579.       char name[256];
  580.       char *cp, *cp2;
  581.       errno = 0;
  582.  
  583.       /* Skip the leading whitespace. */
  584.       for (i = strlen (DIRECTIVE_FORMAT); buf[i] && isspace (buf[i]); i++)
  585.         ;
  586.       if (!buf[i])
  587.         fatal (_("%s:%d: %%Format: no name"), ctx.fullname, line);
  588.  
  589.       /* Copy name. */
  590.       for (j = 0;
  591.            j < sizeof (name) - 1 && buf[i] && !isspace (buf[i]);
  592.            i++)
  593.         name[j++] = buf[i];
  594.       name[j] = '\0';
  595.  
  596.       if (j >= sizeof (name) - 1)
  597.         fatal (_("%s:%d: %%Format: too long name, maxlen=%d"),
  598.            ctx.fullname, line, sizeof (name) - 1);
  599.  
  600.       /* Find the start of the format string. */
  601.       for (; buf[i] && isspace (buf[i]); i++)
  602.         ;
  603.       
  604.       /* Find the end. */
  605.       j = strlen (buf);
  606.       for (j--; isspace (buf[j]) && j > i; j--)
  607.         ;
  608.       j++;
  609.  
  610.       message (2, "%%Format: %s %.*s\n", name, j - i, buf + i);
  611.  
  612.       cp = xmalloc (j - i + 1);
  613.       memcpy (cp, buf + i, j - i);
  614.       cp[j - i] = '\0';
  615.  
  616.       strhash_put (user_strings, name, strlen (name) + 1, cp,
  617.                (void **) &cp2);
  618.       if (cp2)
  619.         fatal (_("%s:%d: %%Format: name \"%s\" is already defined"),
  620.            ctx.fullname, line, name);
  621.  
  622.       /* All done with the `%Format' directive. */
  623.       continue;
  624.     }
  625.  
  626.       /*
  627.        * `%HeaderHeight' directive?
  628.        */
  629. #define DIRECTIVE_HEADERHEIGHT "%HeaderHeight:"
  630.       if (strncmp (buf, DIRECTIVE_HEADERHEIGHT,
  631.            strlen (DIRECTIVE_HEADERHEIGHT)) == 0)
  632.       {
  633.         int i;
  634.  
  635.         /* Find the start of the pts argument. */
  636.         for (i = strlen (DIRECTIVE_HEADERHEIGHT);
  637.          buf[i] && !isspace (buf[i]); i++)
  638.           ;
  639.         if (!buf[i])
  640.           fatal (_("%s:%d: %%HeaderHeight: no argument"), ctx.fullname, line);
  641.  
  642.         d_header_h = atoi (buf + i);
  643.         message (2, "%%HeaderHeight: %d\n", d_header_h);
  644.         continue;
  645.       }
  646.  
  647.       /*
  648.        * `%FooterHeight' directive?
  649.        */
  650. #define DIRECTIVE_FOOTERHEIGHT "%FooterHeight:"
  651.       if (strncmp (buf, DIRECTIVE_FOOTERHEIGHT,
  652.            strlen (DIRECTIVE_FOOTERHEIGHT)) == 0)
  653.     {
  654.       int i;
  655.  
  656.       /* Find the start of the pts argument. */
  657.       for (i = strlen (DIRECTIVE_FOOTERHEIGHT);
  658.            buf[i] && !isspace (buf[i]); i++)
  659.         ;
  660.       if (!buf[i])
  661.         fatal (_("%s:%d: %%FooterHeight: no argument"), ctx.fullname, line);
  662.  
  663.       d_footer_h = atoi (buf + i);
  664.       message (2, "%%FooterHeight: %d\n", d_footer_h);
  665.       continue;
  666.     }
  667.  
  668.       /* Nothing special, just copy it to the output. */
  669.       fputs (buf, ofp);
  670.     }
  671.  
  672.   fclose (fp);
  673.  
  674.   return 1;
  675. }
  676.  
  677.  
  678. int
  679. parse_font_spec (char *spec, char **name_return, double *size_return)
  680. {
  681.   int i;
  682.   char *cp;
  683.  
  684.   /* The `name@ptsize' format? */
  685.   cp = strchr (spec, '@');
  686.   if (cp)
  687.     {
  688.       i = cp - spec;
  689.       if (cp[1] == '\0')
  690.     /* No ptsize after '@'. */
  691.     return 0;
  692.  
  693.       *size_return = atof (cp + 1);
  694.     }
  695.   else
  696.     {
  697.       /* Old `nameptsize' format. */
  698.       i = strlen (spec) - 1;
  699.       if (i <= 0 || !ISNUMBERDIGIT (spec[i]))
  700.     return 0;
  701.  
  702.       for (i--; i >= 0 && ISNUMBERDIGIT (spec[i]); i--)
  703.     ;
  704.       i++;
  705.       *size_return = atof (spec + i);
  706.     }
  707.   
  708.   *name_return = (char *) xcalloc (1, i + 1);
  709.   strncpy (*name_return, spec, i);
  710.  
  711.   message (2, "parse_font_spec(): name=%.*s, size=%g\n", i, *name_return,
  712.        *size_return);
  713.   return 1;
  714. }
  715.  
  716.  
  717. void
  718. read_font_info (void)
  719. {
  720.   AFMFont font;
  721.   int cached = 1;
  722.   int i;
  723.   unsigned int enc_flags = 0;
  724.  
  725.   message (1, _("reading AFM info for font \"%s\"\n"), Fname);
  726.  
  727.   if (accept_composites)
  728.     enc_flags = AFM_ENCODE_ACCEPT_COMPOSITES;
  729.  
  730.   /* Open font */
  731.   if (!strhash_get (afm_cache, Fname, strlen (Fname), (void **) &font))
  732.     {
  733.       AFMError error;
  734.       char buf[256];
  735.  
  736.       /* Couldn't find it from our cache, open it from disk. */
  737.       error = afm_open_font (afm, AFM_I_COMPOSITES, Fname, &font);
  738.       if (error != AFM_SUCCESS)
  739.     {
  740. #define COUR "Courier"
  741.       /*
  742.        * Do not report failures for "Courier*" fonts because 
  743.        * AFM library's default font will fix them.
  744.        */
  745.       if (strncmp (Fname, COUR, strlen (COUR)) != 0)
  746.         message (0, _("couldn't open AFM file for font \"%s\", using default\n"),
  747.              Fname);
  748.       error = afm_open_default_font (afm, &font);
  749.       if (error != AFM_SUCCESS)
  750.         {
  751.           afm_error_to_string (error, buf);
  752.           fatal (_("couldn't open AFM file for the default font: %s"),
  753.              buf);
  754.         }
  755.     }
  756.  
  757.       /* Apply encoding. */
  758.       switch (encoding)
  759.     {
  760.     case ENC_LATIN1:
  761.       (void) afm_font_encoding (font, AFM_ENCODING_LATIN1, enc_flags);
  762.     break;
  763.  
  764.     case ENC_LATIN2:
  765.       (void) afm_font_encoding (font, AFM_ENCODING_LATIN2, enc_flags);
  766.     break;
  767.  
  768.     case ENC_LATIN3:
  769.       (void) afm_font_encoding (font, AFM_ENCODING_LATIN3, enc_flags);
  770.     break;
  771.  
  772.     case ENC_ASCII:
  773.       (void) afm_font_encoding (font, AFM_ENCODING_ASCII, enc_flags);
  774.     break;
  775.  
  776.     case ENC_ASCII_SCANDS:
  777.       /* First apply standard 7bit ASCII encoding. */
  778.       (void) afm_font_encoding (font, AFM_ENCODING_ASCII, enc_flags);
  779.  
  780.     /* Then add those scand characters. */
  781.     for (i = 0; enc_7bit_ascii_scands[i].name; i++)
  782.       (void) afm_font_encode (font, enc_7bit_ascii_scands[i].code,
  783.                   enc_7bit_ascii_scands[i].name, enc_flags);
  784.     break;
  785.  
  786.     case ENC_IBMPC:
  787.       (void) afm_font_encoding (font, AFM_ENCODING_IBMPC, enc_flags);
  788.     break;
  789.  
  790.     case ENC_MAC:
  791.       (void) afm_font_encoding (font, AFM_ENCODING_MAC, enc_flags);
  792.     break; 
  793.  
  794.     case ENC_VMS:
  795.       (void) afm_font_encoding (font, AFM_ENCODING_VMS, enc_flags);
  796.     break; 
  797.  
  798.     case ENC_HP8:
  799.       (void) afm_font_encoding (font, AFM_ENCODING_HP8, enc_flags);
  800.     break;
  801.  
  802.     case ENC_PS:
  803.       /* Let's use font's default encoding -- nothing here. */
  804.       break;
  805.     }
  806.  
  807.       /* Store font information to the AFM cache. */
  808.       if (!strhash_put (afm_cache, Fname, strlen (Fname), font, NULL))
  809.     cached = 0;
  810.     }
  811.  
  812.   /* Read character widths and types. */
  813.   for (i = 0; i < 256; i++)
  814.     {
  815.       AFMNumber w0x, w0y;
  816.  
  817.       (void) afm_font_charwidth (font, Fpt, (unsigned int) i, &w0x, &w0y);
  818.       font_widths[i] = w0x;
  819.           
  820.       if (font->encoding[i] == AFM_ENC_NONE)
  821.     font_ctype[i] = ' ';
  822.       else if (font->encoding[i] == AFM_ENC_NON_EXISTENT)
  823.     font_ctype[i] = '.';
  824.       else
  825.     font_ctype[i] = '*';
  826.     }
  827.  
  828.   font_is_fixed = font->writing_direction_metrics[0].IsFixedPitch;
  829.   font_bbox_lly = font->global_info.FontBBox_lly;
  830.  
  831.   if (!cached)
  832.     (void) afm_close_font (font);
  833. }
  834.  
  835.  
  836. void
  837. download_font (char *name)
  838. {
  839.   AFMError error;
  840.   const char *prefix;
  841.   struct stat stat_st;
  842.   char fname[512];
  843.   unsigned char buf[4096];
  844.   FILE *fp;
  845.   int i;
  846.   char *cp;
  847.  
  848.   /* Get font prefix. */
  849.   error = afm_font_prefix (afm, name, &prefix);
  850.   if (error != AFM_SUCCESS)
  851.     /* Font is unknown, nothing to download. */
  852.     return;
  853.  
  854.   /* Check if we have a font description file. */
  855.  
  856.   /* .pfa */
  857.   sprintf (fname, "%s.pfa", prefix);
  858.   if (stat (fname, &stat_st) != 0)
  859.     {
  860.       /* .pfb */
  861.       sprintf (fname, "%s.pfb", prefix);
  862.       if (stat (fname, &stat_st) != 0)
  863.     /* Couldn't find font description file, nothing to download. */
  864.     return;
  865.     }
  866.  
  867.   /* Ok, fine.  Font was found. */
  868.  
  869.   message (1, _("downloading font \"%s\"\n"), name);
  870.   fp = fopen (fname, "rb");
  871.   if (fp == NULL)
  872.     {
  873.       message (0, _("couldn't open font description file \"%s\": %s\n"),
  874.            fname, strerror (errno));
  875.       return;
  876.     }
  877.  
  878.   /* Dump file. */
  879.   fprintf (ofp, "%%%%BeginResource: font %s\n", name);
  880.  
  881.   /* Check file type. */
  882.   i = fgetc (fp);
  883.   if (i == EOF)
  884.     {
  885.       /* Not much to do here. */
  886.       ;
  887.     }
  888.   else if (i == 128)
  889.     {
  890.       int done = 0;
  891.       unsigned int chunk;
  892.       unsigned int to_read;
  893.       int last_was_cr;
  894.       int j;
  895.  
  896.       /* IBM PC Format */
  897.  
  898.       ungetc (i, fp);
  899.       
  900.       while (!done)
  901.     {
  902.       /* Read 6-byte long header. */
  903.       i = fread (buf, 1, 6, fp);
  904.       if (i != 6)
  905.         break;
  906.  
  907.       chunk = buf[2] | (buf[3] << 8) | (buf[4] << 16) | (buf[5] << 24);
  908.  
  909.       /* Check chunk type. */
  910.       switch (buf[1])
  911.         {
  912.         case 1:        /* ASCII */
  913.           last_was_cr = 0;
  914.           while (chunk > 0)
  915.         {
  916.           to_read = sizeof (buf) < chunk ? sizeof (buf) : chunk;
  917.           i = fread (buf, 1, to_read, fp);
  918.           if (i == 0)
  919.             {
  920.               done = 1;
  921.               break;
  922.             }
  923.  
  924.           /* Check and fix Mac-newlines. */
  925.           for (j = 0; j < i; j++)
  926.             {
  927.               if (j == 0 && last_was_cr && buf[0] != '\n')
  928.             {
  929.               fputc ('\n', ofp);
  930.               fputc (buf[0], ofp);
  931.             }
  932.               else if (buf[j] == '\r' && j + 1 < i
  933.                    && buf[j + 1] != '\n')
  934.             {
  935.               fputc ('\n', ofp);
  936.             }
  937.               else if (buf[j] != '\r')
  938.             fputc (buf[j], ofp);
  939.             }
  940.  
  941.           chunk -= i;
  942.           last_was_cr = (buf[i - 1] == '\r');
  943.         }
  944.           break;
  945.  
  946.         case 2:        /* binary data */
  947.           while (chunk > 0)
  948.         {
  949.           to_read = sizeof (buf) < chunk ? sizeof (buf) : chunk;
  950.           i = fread (buf, 1, to_read, fp);
  951.           if (i == 0)
  952.             {
  953.               done = 1;
  954.               break;
  955.             }
  956.  
  957.           for (j = 0; j < i; j++)
  958.             {
  959.               fprintf (ofp, "%02X", buf[j]);
  960.               if ((j + 1) % 32 == 0)
  961.             fprintf (ofp, "\n");
  962.             }
  963.           chunk -= i;
  964.         }
  965.           break;
  966.  
  967.         case 3:        /* EOF */
  968.           done = 1;
  969.           break;
  970.         }
  971.  
  972.       /* Force a linebreak after each chunk. */
  973.       fprintf (ofp, "\n");
  974.     }
  975.     }
  976.   else
  977.     {
  978.       /* Plain ASCII. */
  979.       ungetc (i, fp);
  980.       while ((i = fread (buf, 1, sizeof (buf), fp)) != 0)
  981.     fwrite (buf, 1, i, ofp);
  982.     }
  983.  
  984.   fprintf (ofp, "%%%%EndResource\n");
  985.  
  986.   /* Remove font from needed resources. */
  987.   (void) strhash_delete (res_fonts, name, strlen (name) + 1, (void **) &cp);
  988.  
  989.   fclose (fp);
  990. }
  991.  
  992.  
  993. char *
  994. escape_string (char *string)
  995. {
  996.   int i, j;
  997.   int len;
  998.   char *cp;
  999.  
  1000.   /* Count the length of the result string. */
  1001.   for (len = 0, i = 0; string[i]; i++)
  1002.     switch (string[i])
  1003.       {
  1004.       case '(':
  1005.       case ')':
  1006.       case '\\':
  1007.     len += 2;
  1008.     break;
  1009.  
  1010.       default:
  1011.     len++;
  1012.       }
  1013.  
  1014.   /* Create result. */
  1015.   cp = xmalloc (len + 1);
  1016.   for (i = 0, j = 0; string[i]; i++)
  1017.     switch (string[i])
  1018.       {
  1019.       case '(':
  1020.       case ')':
  1021.       case '\\':
  1022.     cp[j++] = '\\';
  1023.     /* FALLTHROUGH */
  1024.  
  1025.       default:
  1026.     cp[j++] = string[i];
  1027.     break;
  1028.       }
  1029.   cp[j++] = '\0';
  1030.  
  1031.   return cp;
  1032. }
  1033.  
  1034.  
  1035.  
  1036. /*
  1037.  * Help macros for the format_user_string() function.
  1038.  */
  1039.  
  1040. #define NEED_NBYTES(n)                 \
  1041.   do {                        \
  1042.     if (rbufpos + (n) >= rbuflen)        \
  1043.       {                        \
  1044.         rbuflen += (n) + 1024;            \
  1045.         rbuf = xrealloc (rbuf, rbuflen);    \
  1046.       }                        \
  1047.   } while (0)
  1048.  
  1049. #define APPEND_CH(ch)                \
  1050.   do {                        \
  1051.     NEED_NBYTES (1);                \
  1052.     rbuf[rbufpos++] = (ch);            \
  1053.   } while (0)
  1054.  
  1055. #define APPEND_STR(str)                \
  1056.   do {                        \
  1057.     int len = strlen ((str));            \
  1058.     NEED_NBYTES (len);                \
  1059.     memcpy (rbuf + rbufpos, str, len);        \
  1060.     rbufpos += len;                \
  1061.   } while (0)
  1062.  
  1063. char *
  1064. format_user_string (char *str)
  1065. {
  1066.   char *cp;
  1067.   char *rbuf = NULL;
  1068.   int rbuflen = 0;
  1069.   int rbufpos = 0;
  1070.   int i = 0;
  1071.   int j;
  1072.   char buf[512];
  1073.   char buf2[512];
  1074.  
  1075.   /* Format string. */
  1076.   for (i = 0; str[i] != '\0'; i++)
  1077.     {
  1078.       switch (str[i])
  1079.     {
  1080.     case '%':        /* general state related `%' escapes  */
  1081.       i++;
  1082.       switch (str[i])
  1083.         {
  1084.         case '%':        /* `%%' character `%' */
  1085.           APPEND_CH ('%');
  1086.           break;
  1087.  
  1088.         case 'c':        /* `%c' trailing component of pwd. */
  1089.           getcwd (buf, sizeof (buf));
  1090.           cp = strrchr (buf, '/');
  1091.           if (cp)
  1092.         cp++;
  1093.           else
  1094.         cp = buf;
  1095.           APPEND_STR (cp);
  1096.           break;
  1097.  
  1098.         case 'C':        /* `%C' runtime in `hh:mm:ss' format */
  1099.           sprintf (buf, "%02d:%02d:%02d", run_tm.tm_hour, run_tm.tm_min,
  1100.                run_tm.tm_sec);
  1101.           APPEND_STR (buf);
  1102.           break;
  1103.  
  1104.         case 'd':        /* `%d' current working directory */
  1105.           getcwd (buf, sizeof (buf));
  1106.           APPEND_STR (buf);
  1107.           break;
  1108.  
  1109.         case 'D':
  1110.           if (str[i + 1] == '{')
  1111.         {
  1112.           /* `%D{}' format run date with strftime() */
  1113.           for (j = 0, i += 2;
  1114.                j < sizeof (buf2) && str[i] && str[i] != '}';
  1115.                i++, j++)
  1116.             buf2[j] = str[i];
  1117.           if (str[i] != '}')
  1118.             fatal (_("%%Format: too long format for %%D{} escape"));
  1119.  
  1120.           buf2[j] = '\0';
  1121.           strftime (buf, sizeof (buf), buf2, &run_tm);
  1122.         }
  1123.           else
  1124.         {
  1125.           /* `%D' run date in `yy-mm-dd' format */
  1126.           sprintf (buf, "%02d-%02d-%02d", run_tm.tm_year,
  1127.                run_tm.tm_mon + 1, run_tm.tm_mday);
  1128.         }
  1129.           APPEND_STR (buf);
  1130.           break;
  1131.  
  1132.         case 'E':        /* `%E' run date in `yy/mm/dd' format */
  1133.           sprintf (buf, "%02d/%02d/%02d", run_tm.tm_year,
  1134.                run_tm.tm_mon + 1, run_tm.tm_mday);
  1135.           APPEND_STR (buf);
  1136.           break;
  1137.  
  1138.         case 'F':        /* `%F' run date in `dd.mm.yyyy' format */
  1139.           sprintf (buf, "%d.%d.%d",
  1140.                run_tm.tm_mday,
  1141.                run_tm.tm_mon + 1,
  1142.                run_tm.tm_year+1900);
  1143.           APPEND_STR (buf);
  1144.           break;
  1145.  
  1146.         case 'H':        /* `%H' document title */
  1147.           APPEND_STR (title);
  1148.           break;
  1149.  
  1150.         case 'm':        /* `%m' the hostname up to the first `.' */
  1151.           (void) gethostname (buf, sizeof (buf));
  1152.           cp = strchr (buf, '.');
  1153.           if (cp)
  1154.         *cp = '\0';
  1155.           APPEND_STR (buf);
  1156.           break;
  1157.  
  1158.         case 'M':        /* `%M' the full hostname */
  1159.           (void) gethostname (buf, sizeof (buf));
  1160.           APPEND_STR (buf);
  1161.           break;
  1162.  
  1163.         case 'n':        /* `%n' username */
  1164.           APPEND_STR (passwd->pw_name);
  1165.           break;
  1166.  
  1167.         case 'N':        /* `%N' pw_gecos up to the first `,' char */
  1168.           strcpy (buf, passwd->pw_gecos);
  1169.           cp = strchr (buf, ',');
  1170.           if (cp)
  1171.         *cp = '\0';
  1172.           APPEND_STR (buf);
  1173.           break;
  1174.  
  1175.         case 't':        /* `%t' runtime in 12-hour am/pm format */
  1176.           sprintf (buf, "%d:%d%s",
  1177.                run_tm.tm_hour > 12
  1178.                ? run_tm.tm_hour - 12 : run_tm.tm_hour,
  1179.                run_tm.tm_min,
  1180.                run_tm.tm_hour > 12 ? "pm" : "am");
  1181.           APPEND_STR (buf);
  1182.           break;
  1183.  
  1184.         case 'T':        /* `%T' runtime in 24-hour format */
  1185.           sprintf (buf, "%d:%d", run_tm.tm_hour, run_tm.tm_min);
  1186.           APPEND_STR (buf);
  1187.           break;
  1188.  
  1189.         case '*':        /* `%*' runtime in 24-hour format with secs */
  1190.           sprintf (buf, "%d:%d:%d", run_tm.tm_hour, run_tm.tm_min,
  1191.                run_tm.tm_sec);
  1192.           APPEND_STR (buf);
  1193.           break;
  1194.  
  1195.         case 'W':        /* `%W' run date in `mm/dd/yy' format */
  1196.           sprintf (buf, "%02d/%02d/%02d", run_tm.tm_mon + 1,
  1197.                run_tm.tm_mday, run_tm.tm_year);
  1198.           APPEND_STR (buf);
  1199.           break;
  1200.  
  1201.         default:
  1202.           fatal (_("%%Format: unknown `%%' escape `%c' (%d)"),
  1203.              str[i], str[i]);
  1204.           break;
  1205.         }
  1206.       break;
  1207.  
  1208.     case '$':        /* input file related `$' escapes */
  1209.       i++;
  1210.       switch (str[i])
  1211.         {
  1212.         case '$':        /* `$$' character `$' */
  1213.           APPEND_CH ('$');
  1214.           break;
  1215.  
  1216.         case '%':        /* `$%' current page number */
  1217.           sprintf (buf, "%d", current_pagenum);
  1218.           APPEND_STR (buf);
  1219.           break;
  1220.  
  1221.         case '=':        /* `$=' number of pages in this file */
  1222.           APPEND_CH ('\001');
  1223.           break;
  1224.  
  1225.         case '(':        /* $(ENVVAR)  */
  1226.           for (j = 0, i++;
  1227.            str[i] && str[i] != ')' && j < sizeof (buf) - 1;
  1228.            i++)
  1229.         buf[j++] = str[i];
  1230.  
  1231.           if (str[i] == '\0')
  1232.         fatal (_("%%Format: no closing ')' for $() escape"));
  1233.           if (str[i] != ')')
  1234.         fatal (_("%%Format: too long variable name for $() escape"));
  1235.  
  1236.           buf[j] = '\0';
  1237.  
  1238.           cp = getenv (buf);
  1239.           if (cp == NULL)
  1240.         cp = "";
  1241.           APPEND_STR (cp);
  1242.           break;
  1243.  
  1244.         case 'C':        /* `$C' modtime in `hh:mm:ss' format */
  1245.           sprintf (buf, "%02d:%02d:%02d", mod_tm.tm_hour,
  1246.                mod_tm.tm_min, mod_tm.tm_sec);
  1247.           APPEND_STR (buf);
  1248.           break;
  1249.  
  1250.         case 'D':
  1251.           if (str[i + 1] == '{')
  1252.         {
  1253.           /* `$D{}' format modification date with strftime() */
  1254.           for (j = 0, i += 2;
  1255.                j < sizeof (buf2) && str[i] && str[i] != '}';
  1256.                i++, j++)
  1257.             buf2[j] = str[i];
  1258.           if (str[i] != '}')
  1259.             fatal (_("%%Format: too long format for $D{} escape"));
  1260.  
  1261.           buf2[j] = '\0';
  1262.           strftime (buf, sizeof (buf), buf2, &mod_tm);
  1263.         }
  1264.           else
  1265.         {
  1266.           /* `$D' mod date in `yy-mm-dd' format */
  1267.           sprintf (buf, "%02d-%02d-%02d", mod_tm.tm_year,
  1268.                mod_tm.tm_mon + 1, mod_tm.tm_mday);
  1269.         }
  1270.           APPEND_STR (buf);
  1271.           break;
  1272.  
  1273.         case 'E':        /* `$E' mod date in `yy/mm/dd' format */
  1274.           sprintf (buf, "%02d/%02d/%02d", mod_tm.tm_year,
  1275.                mod_tm.tm_mon + 1, mod_tm.tm_mday);
  1276.           APPEND_STR (buf);
  1277.           break;
  1278.  
  1279.         case 'F':        /* `$F' run date in `dd.mm.yyyy' format */
  1280.           sprintf (buf, "%d.%d.%d",
  1281.                mod_tm.tm_mday,
  1282.                mod_tm.tm_mon + 1,
  1283.                mod_tm.tm_year+1900);
  1284.           APPEND_STR (buf);
  1285.           break;
  1286.  
  1287.         case 't':        /* `$t' runtime in 12-hour am/pm format */
  1288.           sprintf (buf, "%d:%d%s",
  1289.                mod_tm.tm_hour > 12
  1290.                ? mod_tm.tm_hour - 12 : mod_tm.tm_hour,
  1291.                mod_tm.tm_min,
  1292.                mod_tm.tm_hour > 12 ? "pm" : "am");
  1293.           APPEND_STR (buf);
  1294.           break;
  1295.  
  1296.         case 'T':        /* `$T' runtime in 24-hour format */
  1297.           sprintf (buf, "%d:%d", mod_tm.tm_hour, mod_tm.tm_min);
  1298.           APPEND_STR (buf);
  1299.           break;
  1300.  
  1301.         case '*':        /* `$*' runtime in 24-hour format with secs */
  1302.           sprintf (buf, "%d:%d:%d", mod_tm.tm_hour, mod_tm.tm_min,
  1303.                mod_tm.tm_sec);
  1304.           APPEND_STR (buf);
  1305.           break;
  1306.  
  1307.         case 'W':        /* `$W' run date in `mm/dd/yy' format */
  1308.           sprintf (buf, "%02d/%02d/%02d", mod_tm.tm_mon + 1,
  1309.                mod_tm.tm_mday, mod_tm.tm_year);
  1310.           APPEND_STR (buf);
  1311.           break;
  1312.  
  1313.         default:
  1314.           fatal (_("%%Format: unknown `$' escape `%c' (%d)"),
  1315.              str[i], str[i]);
  1316.           break;
  1317.         }
  1318.       break;
  1319.  
  1320.     default:
  1321.       APPEND_CH (str[i]);
  1322.       break;
  1323.     }
  1324.     }
  1325.   APPEND_CH ('\0');
  1326.   
  1327.   /* Escape PS specials. */
  1328.   cp = escape_string (rbuf);
  1329.   xfree (rbuf);
  1330.  
  1331.   return cp;
  1332. }
  1333.  
  1334.  
  1335. void
  1336. parse_key_value_pair (StringHashPtr set, char *kv)
  1337. {
  1338.   char *cp;
  1339.   char key[256];
  1340.  
  1341.   cp = strchr (kv, ':');
  1342.   if (cp == NULL)
  1343.     {
  1344.       if (strhash_delete (set, kv, strlen (kv) + 1, (void **) &cp))
  1345.     xfree (cp);
  1346.     }
  1347.   else
  1348.     {
  1349.       sprintf (key, "%.*s", cp - kv, kv);
  1350.       strhash_put (set, key, strlen (key) + 1, xstrdup (cp + 1),
  1351.            (void **) &cp);
  1352.       if (cp)
  1353.     xfree (cp);
  1354.     }
  1355. }
  1356.  
  1357.  
  1358. int
  1359. count_key_value_set (StringHashPtr set)
  1360. {
  1361.   int i = 0, got, j;
  1362.   char *cp;
  1363.   void *value;
  1364.   
  1365.   for (got = strhash_get_first (set, &cp, &j, &value); got;
  1366.        got = strhash_get_next (set, &cp, &j, &value))
  1367.     i++;
  1368.  
  1369.   return i;
  1370. }
  1371.  
  1372.  
  1373. int
  1374. pathwalk (char *path, PathWalkProc proc, void *context)
  1375. {
  1376.   char buf[512];
  1377.   char *cp;
  1378.   char *cp2;
  1379.   int len, i;
  1380.   
  1381.   for (cp = path; cp; cp = strchr (cp, PATH_SEPARATOR))
  1382.     {
  1383.       if (cp != path)
  1384.     cp++;
  1385.  
  1386.       cp2 = strchr (cp, PATH_SEPARATOR);
  1387.       if (cp2)
  1388.     len = cp2 - cp;
  1389.       else
  1390.     len = strlen (cp);
  1391.  
  1392.       memcpy (buf, cp, len);
  1393.       buf[len] = '\0';
  1394.  
  1395.       i = (*proc) (buf, context);
  1396.       if (i != 0)
  1397.     return i;
  1398.     }
  1399.  
  1400.   return 0;
  1401. }
  1402.  
  1403.  
  1404. int
  1405. file_lookup (char *path, void *context)
  1406. {
  1407.   int len;
  1408.   FileLookupCtx *ctx = context;
  1409.   struct stat stat_st;
  1410.   int i;
  1411.  
  1412.   message (2, "file_lookup(): %s/%s%s\t", path, ctx->name,
  1413.        ctx->suffix);
  1414.  
  1415.   len = strlen (path);
  1416.   if (len && path[len - 1] == '/')
  1417.     len--;
  1418.  
  1419.   sprintf (ctx->fullname, "%.*s/%s%s", len, path, ctx->name, ctx->suffix);
  1420.   
  1421.   i = stat (ctx->fullname, &stat_st) == 0;
  1422.  
  1423.   message (2, "#%c\n", i ? 't' : 'f');
  1424.   return i;
  1425. }
  1426.  
  1427.  
  1428. void
  1429. tilde_subst (char *from, char *to)
  1430. {
  1431.   char *cp;
  1432.   char user[256];
  1433.   int i, j;
  1434.   struct passwd *pswd;
  1435.  
  1436.   if (from[0] != '~')
  1437.     {
  1438.     copy_out:
  1439.       strcpy (to, from);
  1440.       return;
  1441.     }
  1442.  
  1443.   if (from[1] == '/' || from[1] == '\0')
  1444.     {
  1445.       cp = getenv ("HOME");
  1446.       if (cp == NULL)
  1447.     goto copy_out;
  1448.  
  1449.       sprintf (to, "%s%s", cp, from + 1);
  1450.       return;
  1451.     }
  1452.  
  1453.   /* Get user's login name. */
  1454.   for (i = 1, j = 0; from[i] && from[i] != '/'; i++)
  1455.     user[j++] = from[i];
  1456.   user[j++] = '\0';
  1457.  
  1458.   pswd = getpwnam (user);
  1459.   if (pswd)
  1460.     {
  1461.       /* Found passwd entry. */
  1462.       sprintf (to, "%s%s", pswd->pw_dir, from + i);
  1463.       return;
  1464.     }
  1465.  
  1466.   /* No match found. */
  1467.   goto copy_out;
  1468. }
  1469.  
  1470.  
  1471. double
  1472. parse_float (char *string, int units, int horizontal)
  1473. {
  1474.   double val;
  1475.   char *end;
  1476.  
  1477.   val = strtod (string, &end);
  1478.   if (end == string)
  1479.   malformed_float:
  1480.     error (_("malformed float dimension: \"%s\""), string);
  1481.   
  1482.   if (units)
  1483.     {
  1484.       switch (*end)
  1485.     {
  1486.     case 'c':
  1487.       val *= 72 / 2.54;
  1488.       break;
  1489.  
  1490.     case 'p':
  1491.       break;
  1492.  
  1493.     case 'i':
  1494.       val *= 72;
  1495.       break;
  1496.  
  1497.     case '\0':
  1498.       /* FALLTHROUGH */
  1499.  
  1500.     case 'l':
  1501.       if (horizontal)
  1502.         val *= CHAR_WIDTH ('m');
  1503.       else
  1504.         val *= LINESKIP;
  1505.       break;
  1506.  
  1507.     default:
  1508.       goto malformed_float;
  1509.       break;
  1510.     }
  1511.     }
  1512.   else
  1513.     {
  1514.       if (*end != '\0')
  1515.     goto malformed_float;
  1516.     }
  1517.  
  1518.   return val;
  1519. }
  1520.  
  1521.  
  1522. /*
  1523.  * InputStream functions.
  1524.  */
  1525.  
  1526. int
  1527. is_open (InputStream *is, FILE *fp, char *fname, char *input_filter)
  1528. {
  1529.   /* Init stream variables. */
  1530.   is->data_in_buf = 0;
  1531.   is->bufpos = 0;
  1532.   is->nreads = 0;
  1533.   is->unget_ch = EOF;
  1534.  
  1535.   /* Input filter? */
  1536.   if (input_filter)
  1537.     {
  1538.       char *cmd = NULL;
  1539.       int cmdlen;
  1540.       int i, pos;
  1541.  
  1542.       is->is_pipe = 1;
  1543.  
  1544.       if (fname == NULL)
  1545.     fname = input_filter_stdin;
  1546.  
  1547.       /*
  1548.        * Count the initial command length, this will grow dynamically
  1549.        * when file specifier `%s' is encountered from <input_filter>.
  1550.        */
  1551.       cmdlen = strlen (input_filter) + 1;
  1552.       cmd = xmalloc (cmdlen);
  1553.       
  1554.       /* Create filter command. */
  1555.       pos = 0;
  1556.       for (i = 0; input_filter[i]; i++)
  1557.     {
  1558.       if (input_filter[i] == '%')
  1559.         {
  1560.           switch (input_filter[i + 1])
  1561.         {
  1562.         case 's':
  1563.           /* Expand cmd-buffer. */
  1564.           cmdlen += strlen (fname);
  1565.           cmd = xrealloc (cmd, cmdlen);
  1566.  
  1567.           /* Paste filename. */
  1568.           strcpy (cmd + pos, fname);
  1569.           pos += strlen (fname);
  1570.  
  1571.           i++;
  1572.           break;
  1573.  
  1574.         case '%':
  1575.           cmd[pos++] = '%';
  1576.           i++;
  1577.           break;
  1578.  
  1579.         default:
  1580.           cmd[pos++] = input_filter[i];
  1581.           break;
  1582.         }
  1583.         }
  1584.       else
  1585.         cmd[pos++] = input_filter[i];
  1586.     }
  1587.       cmd[pos++] = '\0';
  1588.  
  1589.       is->fp = popen (cmd, "r");
  1590.       xfree (cmd);
  1591.  
  1592.       if (is->fp == NULL)
  1593.     {
  1594.       error (_("couldn't open input filter \"%s\" for file \"%s\": %s"),
  1595.          input_filter, fname ? fname : "(stdin)",
  1596.          strerror (errno));
  1597.       return 0;
  1598.     }
  1599.     }
  1600.   else
  1601.     {
  1602.       /* Just open the stream. */
  1603.       is->is_pipe = 0;
  1604.       if (fp)
  1605.     is->fp = fp;
  1606.       else
  1607.     {
  1608.       is->fp = fopen (fname, "rb");
  1609.       if (is->fp == NULL)
  1610.         {
  1611.           error (_("couldn't open input file \"%s\": %s"), fname,
  1612.              strerror (errno));
  1613.           return 0;
  1614.         }
  1615.     }
  1616.     }
  1617.  
  1618.   return 1;
  1619. }
  1620.  
  1621.  
  1622. void
  1623. is_close (InputStream *is)
  1624. {
  1625.   if (is->is_pipe)
  1626.     pclose (is->fp);
  1627.   else
  1628.     fclose (is->fp);
  1629. }
  1630.  
  1631.  
  1632. int
  1633. is_getc (InputStream *is)
  1634. {
  1635.   int ch;
  1636.  
  1637.   if (is->unget_ch != EOF)
  1638.     {
  1639.       ch = is->unget_ch;
  1640.       is->unget_ch = EOF;
  1641.       return ch;
  1642.     }
  1643.  
  1644.  retry:
  1645.  
  1646.   /* Do we have any data left? */
  1647.   if (is->bufpos >= is->data_in_buf)
  1648.     {
  1649.       /* At the EOF? */
  1650.       if (is->nreads > 0 && is->data_in_buf < sizeof (is->buf))
  1651.     /* Yes. */
  1652.     return EOF;
  1653.  
  1654.       /* Read more data. */
  1655.       is->data_in_buf = fread (is->buf, 1, sizeof (is->buf), is->fp);
  1656.       is->bufpos = 0;
  1657.       is->nreads++;
  1658.  
  1659.       goto retry;
  1660.     }
  1661.  
  1662.   return is->buf[is->bufpos++];
  1663. }
  1664.  
  1665.  
  1666. int
  1667. is_ungetc (int ch, InputStream *is)
  1668. {
  1669.   is->unget_ch = ch;
  1670.   return 1;
  1671. }
  1672.  
  1673.  
  1674. /*
  1675.  * Static functions.
  1676.  */
  1677.  
  1678. static void
  1679. #if defined(HAVE_STDARG_H) && defined(__STDC__) && __STDC__
  1680.   cfg_fatal (char *file, int line, char *fmt, ...)
  1681. #else
  1682.   cfg_fatal (file, line, va_alist)
  1683.      char *file;
  1684.      int line;
  1685.      va_dcl
  1686. #endif
  1687. {
  1688.   va_list args;
  1689. #if defined(HAVE_STDARG_H) && defined(__STDC__) && __STDC__
  1690.   va_start (args, fmt);
  1691. #else
  1692.   char *fmt;
  1693.  
  1694.   va_start (args);
  1695.   fmt = va_arg (args, char *);
  1696. #endif
  1697.  
  1698.   fprintf (stderr, "%s:%s:%d: ", program, file, line);
  1699.  
  1700. #ifdef HAVE_VPRINTF
  1701.   vfprintf (stderr, fmt, args);
  1702. #else
  1703.   _doprnt (fmt, args, stderr);
  1704. #endif
  1705.   va_end (args);
  1706.  
  1707.   fprintf (stderr, "\n");
  1708.   fflush (stderr);
  1709.  
  1710.   exit (1);
  1711. }
  1712.